/* eslint-disable @typescript-eslint/no-explicit-any */
import { Component, cls, type ElementProps, type StyledProps } from '@knno/jsx';
import SVGRemove from '@mdi/svg/svg/close.svg';
import { knnoCheck } from '../../theme/check.css';
import { knnoInput } from '../../theme/input.css';
import { knnoRadio } from '../../theme/radio.css';
import { knnoSwitch } from '../../theme/switch.css';
import { Icon } from '../icon/icon';
import type { InputType } from '../inputType';
import { iconInputStyle } from './input.css';

export function Input(props: ElementProps<HTMLInputElement>) {
	return (<input {...props} class={cls(props.class, knnoInput, 'primary')} />) as HTMLInputElement;
}

export function Check(props: ElementProps<HTMLInputElement>) {
	return (
		<input {...props} type="checkbox" class={cls(props.class, knnoCheck)} />
	) as HTMLInputElement;
}

export function Radio(props: ElementProps<HTMLInputElement>) {
	return (
		<input {...props} type="radio" class={cls(props.class, knnoRadio)} />
	) as HTMLInputElement;
}

export function Switch(props: ElementProps<HTMLInputElement>) {
	return (
		<input {...props} type="checkbox" class={cls(props.class, knnoSwitch)} />
	) as HTMLInputElement;
}

type IconInputEventsProps<T> = {
	onChange?: (this: T, e: Event) => void;
	onInput?: (this: T, e: Event) => void;
	onBlur?: (this: T, e: Event) => void;
	onFocus?: (this: T, e: Event) => void;
	onClick?: (this: T, e: MouseEvent) => void;
	onKeyDown?: (this: T, e: KeyboardEvent) => void;
	onKeyUp?: (this: T, e: KeyboardEvent) => void;
	onKeyPress?: (this: T, e: KeyboardEvent) => void;
	onPointerDown?: (this: T, e: PointerEvent) => void;
	onPointerUp?: (this: T, e: PointerEvent) => void;
	onPointerCancel?: (this: T, e: PointerEvent) => void;
	onPointerMove?: (this: T, e: PointerEvent) => void;
	onIconClick?: (this: T, e: MouseEvent) => void;
	onSuffixIconClick?: (this: T, e: MouseEvent) => void;
	onClear?: (this: T) => void;
};

export type IconInputProps<T extends IconInput<any>> = StyledProps<T> & {
	icon?: string;
	suffixIcon?: string;
	autoComplete?: AutoFill;
	autoFocus?: boolean;
	type?: InputType;
	readOnly?: boolean;
	disabled?: boolean;
	value?: string;
	defaultValue?: string;
	required?: boolean;
	name?: string;
	inputMode?: string;
	maxLength?: number;
	minLength?: number;
	min?: string;
	max?: string;
	pattern?: string;
	placeholder?: string;
	showClear?: boolean;
} & IconInputEventsProps<T>;
// = IconInputProps<IconInput<any, any>>
// = IconInput<P, any>
export class IconInput<
	P extends IconInputProps<any> = IconInputProps<IconInput<any>>,
	C extends IconInput<any> = IconInput<any, any>,
> extends Component<P & IconInputEventsProps<C>, C, HTMLDivElement> {
	protected get input(): HTMLInputElement {
		return this.el.querySelector('input')!;
	}

	render() {
		return (
			<div class={cls(this.props.class, iconInputStyle)} style={this.props.style}>
				{this.props.icon ? (
					<Icon
						class="prefix-icon"
						svg={this.props.icon}
						onClick={(evt) => (this as IconInput).call('onIconClick', evt)}
					/>
				) : (
					''
				)}
				<input
					type={this.props.type ?? 'text'}
					autocomplete={this.props.autoComplete}
					autofocus={this.props.autoFocus ?? false}
					value={this.props.value ?? ''}
					readOnly={this.props.readOnly}
					disabled={this.props.disabled}
					defaultValue={this.props.defaultValue ?? ''}
					name={this.props.name}
					required={this.props.required}
					inputMode={this.props.inputMode}
					maxlength={this.props.maxLength ?? -1}
					minlength={this.props.minLength}
					min={this.props.min}
					max={this.props.max}
					pattern={this.props.pattern}
					placeholder={this.props.placeholder ?? ''}
					onChange={(evt) => (this as IconInput).call('onChange', evt)}
					onInput={(evt) => (this as IconInput).call('onInput', evt)}
					onBlur={(evt) => (this as IconInput).call('onBlur', evt)}
					onFocus={(evt) => (this as IconInput).call('onFocus', evt)}
					onClick={(evt) => (this as IconInput).call('onClick', evt)}
					onKeyDown={(evt) => (this as IconInput).call('onKeyDown', evt)}
					onKeyUp={(evt) => (this as IconInput).call('onKeyUp', evt)}
					onKeyPress={(evt) => (this as IconInput).call('onKeyPress', evt)}
					onPointerDown={(evt) => (this as IconInput).call('onPointerDown', evt)}
					onPointerUp={(evt) => (this as IconInput).call('onPointerUp', evt)}
					onPointerCancel={(evt) => (this as IconInput).call('onPointerCancel', evt)}
					onPointerMove={(evt) => (this as IconInput).call('onPointerMove', evt)}
				/>
				<Icon
					class={cls(this.props.showClear ? 'show-clear' : '', 'clear-icon')}
					svg={SVGRemove}
					size="1.4em"
					onClick={() => {
						this.input.value = '';
						(this as IconInput).call('onClear');
						(this as IconInput).call('onInput', new Event('input'));
					}}
				/>
				{this.props.suffixIcon ? (
					<Icon
						class="suffix-icon"
						svg={this.props.suffixIcon}
						onClick={(evt) => (this as IconInput).call('onSuffixIconClick', evt)}
					/>
				) : (
					''
				)}
			</div>
		);
	}

	setIcon(icon: string | null) {
		this.el.querySelector(':scope>.prefix-icon')?.remove();
		if (icon) {
			this.el.insertBefore(
				<Icon
					class="prefix-icon"
					svg={icon}
					onClick={(evt) => (this as IconInput).call('onIconClick', evt)}
				/>,
				this.el.firstChild,
			);
		}
	}
	setSuffixIcon(icon: string | null) {
		this.el.querySelector(':scope>.suffix-icon')?.remove();
		if (icon) {
			this.el.appendChild(
				<Icon
					class="suffix-icon"
					svg={icon}
					onClick={(evt) => (this as IconInput).call('onSuffixIconClick', evt)}
				/>,
			);
		}
	}

	get autoComplete(): AutoFill {
		return this.input.autocomplete;
	}
	setAutoComplete(value: AutoFill) {
		this.input.autocomplete = value;
		return this;
	}

	get autoFocus(): boolean {
		return this.input.autofocus;
	}
	setAutoFocus(value: boolean) {
		this.input.autofocus = value;
		return this;
	}

	get type(): InputType {
		return this.input.type as InputType;
	}
	setType(value: InputType) {
		this.input.type = value;
		return this;
	}

	get readOnly(): boolean {
		return this.input.readOnly;
	}
	setReadOnly(value: boolean) {
		this.input.readOnly = value;
		return this;
	}

	get disabled(): boolean {
		return this.input.disabled;
	}
	setDisabled(value: boolean) {
		this.input.disabled = value;
		return this;
	}

	get value(): string {
		return this.input.value;
	}
	setValue(value: string) {
		this.input.value = value;
		return this;
	}

	get defaultValue(): string {
		return this.input.defaultValue;
	}
	setDefaultValue(value: string) {
		this.input.defaultValue = value;
		return this;
	}

	get required(): boolean {
		return this.input.required;
	}
	setRequired(value: boolean) {
		this.input.required = value;
		return this;
	}

	get name(): string {
		return this.input.name;
	}
	setName(value: string) {
		this.input.name = value;
		return this;
	}

	get inputMode(): string {
		return this.input.inputMode;
	}
	setInputMode(value: string) {
		this.input.inputMode = value;
		return this;
	}

	get maxLength(): number {
		return this.input.maxLength;
	}
	setMaxLength(value: number) {
		this.input.maxLength = value;
		return this;
	}

	get minLength(): number {
		return this.input.minLength;
	}
	setMinLength(value: number) {
		this.input.minLength = value;
		return this;
	}

	get min(): string {
		return this.input.min;
	}
	setMin(value: string) {
		this.input.min = value;
		return this;
	}

	get max(): string {
		return this.input.max;
	}
	setMax(value: string) {
		this.input.max = value;
		return this;
	}

	get pattern(): string {
		return this.input.pattern;
	}
	setPattern(value: string) {
		this.input.pattern = value;
		return this;
	}

	get placeholder(): string {
		return this.input.placeholder;
	}
	setPlaceholder(value: string) {
		this.input.placeholder = value;
		return this;
	}

	setShowClear(value: boolean): this {
		const clearIcon = this.el.querySelector(':scope>.clear-icon');
		if (!value) {
			clearIcon?.classList.remove('show-clear');
		} else {
			clearIcon?.classList.add('show-clear');
		}
		return this;
	}

	focus(): this {
		this.input.focus();
		return this;
	}

	blur(): this {
		this.input.blur();
		return this;
	}

	select(): this {
		this.input.select();
		return this;
	}

	setSelectionRange(start: number, end: number, direction?: 'forward' | 'backward' | 'none'): this {
		this.input.setSelectionRange(start, end, direction);
		return this;
	}

	setCustomValidity(error: string): this {
		this.input.setCustomValidity(error);
		return this;
	}

	checkValidity(): boolean {
		return this.input.checkValidity();
	}

	reportValidity(): boolean {
		return this.input.reportValidity();
	}

	onChange(callback: ((this: C, e: Event) => void) | undefined) {
		this.props.onChange = callback;
	}
	onInput(callback: ((this: C, e: Event) => void) | undefined) {
		this.props.onInput = callback;
	}
	onBlur(callback: ((this: C, e: Event) => void) | undefined) {
		this.props.onBlur = callback;
	}
	onFocus(callback: ((this: C, e: Event) => void) | undefined) {
		this.props.onFocus = callback;
	}
	onClick(callback: ((this: C, e: MouseEvent) => void) | undefined) {
		this.props.onClick = callback;
	}
	onKeyDown(callback: ((this: C, e: KeyboardEvent) => void) | undefined) {
		this.props.onKeyDown = callback;
	}
	onKeyUp(callback: ((this: C, e: KeyboardEvent) => void) | undefined) {
		this.props.onKeyUp = callback;
	}
	onKeyPress(callback: ((this: C, e: KeyboardEvent) => void) | undefined) {
		this.props.onKeyPress = callback;
	}
	onPointerDown(callback: ((this: C, e: PointerEvent) => void) | undefined) {
		this.props.onPointerDown = callback;
	}
	onPointerUp(callback: ((this: C, e: PointerEvent) => void) | undefined) {
		this.props.onPointerUp = callback;
	}
	onPointerCancel(callback: ((this: C, e: PointerEvent) => void) | undefined) {
		this.props.onPointerCancel = callback;
	}
	onPointerMove(callback: ((this: C, e: PointerEvent) => void) | undefined) {
		this.props.onPointerMove = callback;
	}
	onIconClick(callback: ((this: C, e: MouseEvent) => void) | undefined) {
		this.props.onIconClick = callback;
	}
	onSuffixIconClick(callback: ((this: C, e: MouseEvent) => void) | undefined) {
		this.props.onSuffixIconClick = callback;
	}
	onClear(callback: ((this: C) => void) | undefined) {
		this.props.onClear = callback;
	}
}
